package com.westcom.vulb.network.websocket;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Random;

import org.json.JSONException;
import org.json.JSONObject;

import com.westcom.vulb.bean.notify_node;
import com.westcom.vulb.protocal.http.Response;

import de.tavendo.autobahn.WebSocket.WebSocketConnectionObserver;
import de.tavendo.autobahn.WebSocketConnection;
import de.tavendo.autobahn.WebSocketException;

import android.os.Message;
import android.util.Log;

public class VulbWebsocket 
{
	private static VulbWebsocket m_instance = null;
	private WebSocketConnection m_wsc = null;
	
	private ArrayList<notify_node> m_state_changed_client_list = new ArrayList<notify_node>();
	private status m_status = status.disconnected;
	
	private ArrayList<sheetEventNode> m_sheet_changed_client_list = new ArrayList<sheetEventNode>();
	
	public static String protocol = "wss";
	public static String host = "://vulb.jp:";
	public static int port = 8443;
	public static String root_path = "/knowledge/";
	
	public static String base_url = protocol+host+port+root_path;
	
	private static String ws_address = base_url+"ws/servcie";
	
	private HashMap<String,Message> mClientResult = new HashMap<String,Message>();
	
	public static int SUCCESS  = 0;
	public static int WS_ID_DUPLICATED_ERROR = -1;
	public static int WS_ID_NOT_EXISTED_ERROR = -2;
	public static int WS_STATUS_ERROR = -3;
	public static int JSON_ERROR = -4;
	
	
	private WebSocketConnectionObserver m_ws_handler = new WebSocketConnectionObserver()
	{
		@Override
		public void onBinaryMessage(byte[] payload)
		{}
		@Override
		public void onOpen()
		{
			Log.e("ws","ws opened");
			notifyStatusChanged(status.connected);
		}
		@Override
		public void onRawTextMessage(byte[] payload)
		{}
		@Override
		public void onTextMessage(String payload)
		{
			Log.e("ws","ws text message : "+payload);
			Response res = Response.parse(payload);
			if(res == null)
			{
				return;
			}
			if(res.action.get("type").equals("ans"))
			{
				processAns(res);
			}else if(res.action.get("type").equals("bc"))
			{
				processBc(res);
			}
		}
		@Override
		public void onClose(WebSocketCloseNotification code, String reason) {
			// TODO Auto-generated method stub
			Log.e("ws","ws closed");
			notifyStatusChanged(status.disconnected);
		}
	};
	public static enum status
	{
		disconnected,
		connecting,
		connected,
		logined,
	};
	public status getStatus()
	{
		return this.m_status;
	}
	public void registerForStatusChanged(notify_node node)
	{
		m_state_changed_client_list.add(node);
		notifyStatusChanged(m_status);
	}
	private void notifyStatusChanged(status new_status)
	{
		if(m_status == new_status)
		{
			return;
		}
		switch(new_status)
		{
			case disconnected:
			{
			}break;
			case connecting:
			{}break;
			case connected:
			{}break;
		}
		m_status = new_status;
		Message msg = null;
		for(notify_node node:m_state_changed_client_list)
		{
			msg = node.m_handler.obtainMessage(node.m_mesage);
			msg.obj = m_status;
			msg.sendToTarget();
		}
	}
	private VulbWebsocket()
	{
		m_wsc = new WebSocketConnection();
		this.connect();
	}
	public void connect()
	{
		if(m_status == status.disconnected)
		{
			new Thread
			(
				new Runnable()
				{
					public void run()
					{
						try 
						{
							notifyStatusChanged(status.connecting);
							URI u = null;
							try 
							{
								 u = new URI(ws_address);
							} catch (URISyntaxException e) 
							{
								// TODO Auto-generated catch block
								e.printStackTrace();
							}
							m_wsc.connect(u, m_ws_handler);
						} catch (WebSocketException e) 
						{
							// TODO Auto-generated catch block
							e.printStackTrace();
							notifyStatusChanged(status.disconnected);
							return;
						}
						return;
					}
				}
			).start();
		}
	}
	public int send(String payload,String id,Message msg)
	{	
		int ret = SUCCESS;
		if(m_status != status.connected)
		{
			return WS_STATUS_ERROR;
		}
		if(msg != null)
		{
			mClientResult.put(id, msg);
		}
		m_wsc.sendTextMessage(payload);
		return ret;
	}
	public static VulbWebsocket getInstance()
	{
		if(null == m_instance)
		{
			m_instance = new VulbWebsocket();
		}
		return m_instance;
	}
	public int registerForSheetEvent(int sheetId,notify_node node,int wsId)
	{
		boolean isRegistered = false;
		for(sheetEventNode se_node:m_sheet_changed_client_list)
		{
			if(se_node.mWsId == wsId)
			{
				return WS_ID_DUPLICATED_ERROR;//register duplicated
			}
			if(se_node.mSheetId == sheetId)
			{
				isRegistered = true;
			}
		}
		if(!isRegistered)
		{
			//register to server;
			JSONObject req_js = new JSONObject();       
	        try 
			{
				JSONObject action = new JSONObject();
				action.put("id","entrySheet");
				action.put("code","entrySheet");
				action.put("type","req");
				
				JSONObject params = new JSONObject();
				params.put("sheetId",sheetId);
				
				req_js.put("action", action);
				req_js.put("params", params);
			} catch (JSONException e1) 
			{
				// TODO Auto-generated catch block
				e1.printStackTrace();
				return JSON_ERROR;
			}
	        this.send(req_js.toString(),"entrySheet", null);
		}
		m_sheet_changed_client_list.add(new sheetEventNode(sheetId,node,wsId));
		return SUCCESS;
	}
	public int unregisterForSheetEvent(int sheetId,int wsId)
	{
		boolean isRegisteredForOtherUser = false;
		int index = -1;
		for(int i = 0;i<m_sheet_changed_client_list.size();i++)
		{
			sheetEventNode se_node = m_sheet_changed_client_list.get(i);
			if(	se_node.mSheetId == sheetId 
				&&wsId != se_node.mWsId)
			{
				isRegisteredForOtherUser = true;
			}
			if(wsId == se_node.mWsId)
			{
				index = i;
			}
		}
		if(index == -1)
		{
			return WS_ID_NOT_EXISTED_ERROR;//not registered
		}
		if(!isRegisteredForOtherUser)// other user not register for this sheet event
		{
			//unregister to server;
			JSONObject req_js = new JSONObject();       
	        try 
			{
				JSONObject action = new JSONObject();
				action.put("code","exitSheet");
				action.put("type","req");
				
				JSONObject params = new JSONObject();
				params.put("sheetId",sheetId);
				
				req_js.put("action", action);
				req_js.put("params", params);
			} catch (JSONException e1) 
			{
				// TODO Auto-generated catch block
				e1.printStackTrace();
				return JSON_ERROR;
			}
	        this.send(req_js.toString(),"entrySheet", null);
		}
		m_sheet_changed_client_list.remove(index);
		return SUCCESS;
	}
	private class sheetEventNode
	{
		public int mSheetId;
		public notify_node mNode;
		public int mWsId;
		public sheetEventNode(int sheetId,notify_node node,int wsId)
		{
			this.mSheetId = sheetId;
			this.mNode = node;
			mWsId = wsId;
		}
	}
	private void processAns(Response res)
	{
		if(	res.action.get("code").equals("login"))
		{
			if(res.errno == Response.SUCCESS)
			{
				notifyStatusChanged(status.logined);
			}else
			{
				notifyStatusChanged(status.connected);
			}
		}
		HashMap<String,Object>a = res.action;
		String id = (String)a.get("id");
		if(id == null)
		{
			return;
		}
		Message msg = mClientResult.get(id);
		if(null == msg)
		{
			return;
		}
		msg.obj = res;
		msg.sendToTarget();
		mClientResult.remove(id);
	}
	private void processBc(Response res)
	{
		notifySheetEvent(res);
	}
	private void notifySheetEvent(Response res)
	{
		int sheetId = (Integer)res.data.get("sheetId");
		Message msg = null;
		for(sheetEventNode node:m_sheet_changed_client_list)
		{
			if(sheetId == node.mSheetId)
			{
				msg = node.mNode.m_handler.obtainMessage(node.mNode.m_mesage);
				msg.obj = res;
				msg.arg1 = sheetId;
				msg.arg2 = node.mWsId;
				msg.sendToTarget();
			}
		}
	}
	public long getWsId()
	{
		Random r = new Random();
		return r.nextLong();
	}
}
